home *** CD-ROM | disk | FTP | other *** search
/ Scene Storm / Scene Storm - Volume 1.iso / coding / c / xvectors / vector0.04.c < prev    next >
C/C++ Source or Header  |  1995-11-05  |  14KB  |  427 lines

  1. /****************************************************************************/
  2. /*                                        */
  3. /*                      3D-Rotaion Routines                */
  4. /*                                        */
  5. /*    CrEatOr:    LarZ SamuelssoN                  AD: 1993         */
  6. /*                                        */
  7. /*  Version 0.04: Removed all comments. Read 'em in earlier versions.          */ 
  8. /*          This is probably as close to the asm-version we'll get    */
  9. /*    on      without using any asm-code...                    */
  10. /*    public      What I have done is to move as much as possible into the  */
  11. /*    demand      main loop... Function calls cost valuable time...         */
  12. /*          And as asm won't even get close to floating-point         */
  13. /*          numbers so I converted all routines to integer math.        */
  14. /*           This is the last production from me before the summer        */
  15. /*          holidays. But look out for stuff on the Amiga from me     */
  16. /*           this summer.                             */
  17. /*           I'll be back in the Unix-World with new ideas this         */
  18. /*           autumn though....    C Ya                    */
  19. /*                                        */
  20. /*    LAteRZ:      Decided to add some comments after all :)            */
  21. /*                                        */
  22. /*   COPYRIGHT      Vector0.01 - 0.04.c are SpreadWare                */
  23. /*    NOTICE         If you like 'em, spread 'em                */
  24. /*                                        */
  25. /****************************************************************************/
  26.  
  27. #include <X11/Xlib.h>    
  28. #include <X11/X.h>    
  29. #include <stdio.h>    
  30. #include <math.h>    
  31. #include <stdlib.h>    
  32.  
  33. #define    CORNERS 8    /* Number of Corners in the object (Cube)    */
  34. #define DIM    3    /* Number of Coordinates             */
  35. #define PTS    4    /* Number of Points to contain a Surface    */
  36. #define LSIZE    512    /* Number of entrys in the Sine-Table         */
  37. #define RSIZE    29000    /* Number of entrys in the Division-Table    */
  38. #define fe    13    /* Number of shifts (float ~> int)        */
  39. #define fak    8192    /* 2^fe                        */
  40.  
  41. void Cls( void );
  42.  
  43. void Init( void );
  44.  
  45. void Exit( void );
  46.  
  47. void WinInit( int, int, unsigned, unsigned );
  48.  
  49. void SetCol( char * );
  50.  
  51. void ClearArea( unsigned short, unsigned short, unsigned, unsigned );
  52.  
  53. void DrawSolid( short, short, short, short, 
  54.         short, short, short, short, 
  55.         unsigned short, unsigned short );
  56.  
  57. Display         *dpy;
  58. XPoint            mypoints[ PTS ];
  59. XColor             xcolour;
  60. GC             gc;
  61. XGCValues         xgcvalues;
  62. XSetWindowAttributes     xsetwattrs;
  63. Window            win;
  64. int            scrn, pixel;
  65.  
  66. int main (void)
  67. {
  68.     /* loop variable */
  69.  
  70.     unsigned short    i;
  71.     
  72.     /*                     center of rotation              */
  73.     /* if you are using WinInit( ) these should be x = y ~= scale */
  74.  
  75.       unsigned short    x = 600;
  76.     unsigned short    y = 400;
  77.  
  78.     /* initial angles for the object ( rem: 2pi = 511 ) */
  79.     /*              sine-counters            */
  80.  
  81.     unsigned short    wx = 0;
  82.     unsigned short    wy = 0;        
  83.     unsigned short    wz = 0;        
  84.  
  85.     /*            cosine-counters            */
  86.  
  87.     unsigned short     vx = wx + LSIZE/4;
  88.     unsigned short     vy = wy + LSIZE/4;
  89.     unsigned short    vz = wz + LSIZE/4;
  90.  
  91.     /* rotation velocity around the different axises */
  92.  
  93.     unsigned short     sx = 1;
  94.     unsigned short     sy = 2;
  95.     unsigned short     sz = 1;
  96.  
  97.     /* perspective depth */
  98.  
  99.     int     depth = 5;
  100.     int     depte = depth * fak;
  101.  
  102.     /* enlargement in pixels ( gives sidelenght of cube = 300 pixels ) */
  103.     
  104.     int    scale = 150;
  105.  
  106.     /* base vectors ( the three unit-vectors in the euclidian space ) */
  107.  
  108.     int     e1[ DIM ]; 
  109.     int    e2[ DIM ]; 
  110.     int    e3[ DIM ]; 
  111.  
  112.     /* holds an int temporarily */
  113.  
  114.     int     temp;
  115.  
  116.     /* cube[][] holds the position of the cube's corners */
  117.     /*         v[][] is cube[][] with perspective          */
  118.  
  119.     int    cube[ CORNERS ][ DIM ];
  120.     int        v[ CORNERS ][ DIM ];
  121.  
  122.     /* num is used in the hidden-plane-removal part to check */
  123.     /* whether a side should be drawn or not         */
  124.  
  125.     int     num = fak / depth;
  126.  
  127.     /* for use in ClearArea( ) */
  128.  
  129.     unsigned a = 2*scale;
  130.     unsigned b = 2*a;
  131.  
  132.     /* sine-table and division-table ( see COMMENTS ) */
  133.  
  134.     int     sinT[ LSIZE ];
  135.     int     divT[ RSIZE ];
  136.  
  137.     /* creating the precalculated tables */
  138.  
  139.     for ( i=0; i < LSIZE; i++ ) 
  140.         sinT[i] = fak * sin( 6.28 / LSIZE * i );
  141.  
  142.     for ( i=0; i < RSIZE; i++ )
  143.         divT[i] = fak * depte / ( depte - RSIZE/2 + i );
  144.  
  145.     Init( );
  146.  
  147.     /* if you want the cube in a window uncomment the following line */
  148.     /* WinInit( x, y, a, b );                      */
  149.  
  150.     Cls( ); 
  151.  
  152.     while (4711)
  153.     { 
  154.         /*              ROTATION            */
  155.         /* Remember to keep LSIZE as an exponential of 2 as the    */
  156.         /* following won't work else. Instead of checking if v  */
  157.         /* and w are out of range I 'AND' them with LSIZE-1.     */
  158.         /* This will always keep them in range, because the     */
  159.         /* binary representation of LSIZE-1 contains only 1's    */
  160.         /* and as long as w and v are smaller than LSIZE-1 they */
  161.         /* will be unchanged, but if they get larger they'll    */
  162.         /* start again from a low value (not necessarily 0 as   */
  163.         /* in the previous version, so this is a small bug-fix  */
  164.         /* as well as a speed-up)                */
  165.  
  166.         vx = (vx + sx) & LSIZE - 1;
  167.         vy = (vy + sy) & LSIZE - 1;
  168.         vz = (vz + sz) & LSIZE - 1;
  169.         wx = (wx + sx) & LSIZE - 1;
  170.         wy = (wy + sy) & LSIZE - 1;
  171.         wz = (wz + sz) & LSIZE - 1;
  172.  
  173.         /* After each multiplication I'll get something of the     */
  174.         /* size fak^2, so I have to shift the values back to     */
  175.         /* something proportional to fak. Note that I get rid   */
  176.         /* of the divisions this way.                */
  177.  
  178.         e1[0] = -sinT[wz] * sinT[wx] >> fe;
  179.           e1[1] =  sinT[vz] * sinT[wx] >> fe; 
  180.           e1[2] =  sinT[vy] * sinT[vx] >> fe;
  181.         e2[0] = -sinT[wz] * sinT[vx] >> fe;
  182.             e2[1] =  sinT[vz] * sinT[vx] >> fe;
  183.           e2[2] = -sinT[vy] * sinT[wx] >> fe;
  184.           e3[0] =  sinT[vz] * sinT[vy] >> fe;
  185.           e3[1] =  sinT[wz] * sinT[vy] >> fe;
  186.           e3[2] =  sinT[wy];
  187.  
  188.         /* I am using the temp-variable because else I would     */
  189.         /* have gotten something proportional to fak^3 and      */
  190.         /* fak wouldn't have to be that large to make this an   */
  191.         /* integer overflow.                    */
  192.  
  193.         temp  = -sinT[vz] * sinT[wy] >> fe;
  194.         temp  =      temp * sinT[vx] >> fe;
  195.         e1[0] =     e1[0] + temp;
  196.  
  197.         temp  = -sinT[wz] * sinT[wy] >> fe;
  198.         temp  =      temp * sinT[vx] >> fe;
  199.         e1[1] =     e1[1] + temp;
  200.  
  201.         temp  =  sinT[vz] * sinT[wy] >> fe;
  202.         temp  =      temp * sinT[wx] >> fe;
  203.         e2[0] =     e2[0] + temp;
  204.  
  205.         temp  =  sinT[wz] * sinT[wy] >> fe;
  206.         temp  =      temp * sinT[wx] >> fe;
  207.         e2[1] =     e2[1] + temp; 
  208.  
  209.         /* Saving adds is also useful, at least for objects     */
  210.         /* with many corners. What I have done is to use the     */
  211.         /* symmetry of the corner's coordinates in the cube.     */
  212.         /* If you look at the previous versions you'll easily   */
  213.         /* see that the following holds.            */
  214.  
  215.         for ( i=0; i<DIM; i++ )
  216.         {
  217.             cube[0][i] =   e1[i] + e2[i] + e3[i];
  218.             cube[1][i] =   e1[i] - e2[i] + e3[i];
  219.             cube[2][i] =   e1[i] - e2[i] - e3[i];
  220.             cube[3][i] =   e1[i] + e2[i] - e3[i];
  221.  
  222.             cube[4][i] = - cube[1][i];
  223.             cube[5][i] = - cube[2][i];
  224.             cube[6][i] = - cube[3][i];
  225.             cube[7][i] = - cube[0][i];
  226.         }
  227.  
  228.         /*            PERSPECTIVE            */
  229.         /* Division is one of the slowest operations you can     */
  230.         /* perform, so you can do almost whatever it takes to     */
  231.         /* get rid of them. My solution was to precalculate all    */
  232.         /* possible divisions and put them in a table. Again    */
  233.         /* using integer math I have to shift the values back    */
  234.         /* to something proportional to fak.            */
  235.         /* Also see COMMENTS on this subject.            */
  236.  
  237.         for ( i=0; i <CORNERS; i++ )
  238.         {
  239.             v[i][0] = cube[i][0] * divT[ RSIZE/2 - 
  240.                              cube[i][2] ] >> fe;
  241.             v[i][1] = cube[i][1] * divT[ RSIZE/2 - 
  242.                              cube[i][2] ] >> fe;
  243.         }
  244.         
  245.         /*               SCALING            */
  246.         /* If you want to have any sidelenght you want you can    */
  247.         /* do the scaling as follows. But then you'll have to     */
  248.         /* do a lot of mulsing and that's BAD for speed. So,     */
  249.         /* what you might do instead is to play with the        */
  250.         /* shift-value (fe).                     */
  251.         /* Anyhow, here is where I scale everything back down     */
  252.         /* to it's ususal proportions, scale * v[][] is prop to */
  253.         /* scale * fak and therefore I have to shift the result */
  254.         /* to get a 'normal' (~1) value.            */
  255.  
  256.         for ( i=0; i<CORNERS; i++ )
  257.         {
  258.             v[i][0] = scale * v[i][0] >> fe;
  259.             v[i][1] = scale * v[i][1] >> fe;
  260.         }
  261.  
  262.         /*               DRAWING            */
  263.         /* Instead of using Cls( ) or ClearSOb( ) I now use a     */
  264.         /* a function that clears a rectangle around the cube.    */
  265.         /* I also removed some of the SetCol( )'s to save time. */
  266.         /*                            */
  267.         /* BUG: ClearArea( ) should be perspective-sensitive    */
  268.         /*    as the following  won't work if depth = 3.    */
  269.  
  270.         ClearArea( x, y, a, b );
  271.  
  272.         SetCol("blue");
  273.         if ( e1[ DIM-1 ] > num )
  274.             DrawSolid( v[0][0], v[0][1], v[1][0], v[1][1], 
  275.                    v[2][0], v[2][1], v[3][0], v[3][1], x, y );
  276.         if ( e1[ DIM-1 ] < -num )
  277.             DrawSolid( v[4][0], v[4][1], v[5][0], v[5][1], 
  278.                    v[6][0], v[6][1], v[7][0], v[7][1], x, y );
  279.  
  280.         SetCol("cornflower blue");
  281.         if ( e2[ DIM-1 ] > num )
  282.             DrawSolid( v[0][0], v[0][1], v[3][0], v[3][1], 
  283.                    v[4][0], v[4][1], v[5][0], v[5][1], x, y );
  284.         if ( e2[ DIM-1 ] < -num )
  285.             DrawSolid( v[1][0], v[1][1], v[2][0], v[2][1], 
  286.                    v[7][0], v[7][1], v[6][0], v[6][1], x, y );
  287.  
  288.         SetCol("darkslateblue");
  289.         if ( e3[ DIM-1 ] > num )
  290.             DrawSolid( v[0][0], v[0][1], v[1][0], v[1][1], 
  291.                    v[6][0], v[6][1], v[5][0], v[5][1], x, y );
  292.         if ( e3[ DIM-1 ] < -num )
  293.             DrawSolid( v[3][0], v[3][1], v[2][0], v[2][1], 
  294.                    v[7][0], v[7][1], v[4][0], v[4][1], x, y );
  295.     }
  296.  
  297.     Exit( );
  298.       return 0;
  299. }
  300.  
  301. /*                COMMENTS                */
  302. /* In the released versions I have chosen to use the simple standard     */
  303. /* way of doing rotation, i e using a 3D-rotation-matrix. I tried to    */
  304. /* optimize the code as well as possible and in the same time make it     */
  305. /* asm-friendly. I wish there were some way of optimizing the drawing     */
  306. /* routines. One way of doing this would be to use Double Buffering,     */
  307. /* but I haven't got a clue of how to do that under X. So, if anyone    */
  308. /* feel up to it, include multibuf.h and start coding (it sure would     */
  309. /* be nice to get rid of that flicker).                    */
  310. /* Time to calculate the number of operations I use in the main loop    */
  311. /*                                     */
  312. /*     48     multiplications                        */
  313. /*    46    adds and subs                        */
  314. /*    48    shifts (each 13 times)                    */
  315. /*     6     ands                            */
  316. /*     6     compares                        */
  317. /*     x     drawing stuffs                        */
  318. /*                                    */
  319. /* I bet there are loads of better algorithms than mine out there, but  */
  320. /* I hope I have given you a clue of how things are done.         */
  321. /*                                    */
  322. /*              IMPROVEMENT SUGGESTIONS            */
  323. /* First, use another algorithm to generate the location of the three   */
  324. /* unit-vectors. Using three 2D-transformations you will get down to    */
  325. /* 12 muls instead of my 16 in that part. Another way of doing it is by */
  326. /* using spherical coordinates:     x = rsin(s)cos(t)            */
  327. /*                 y = rsin(s)sin(t)            */
  328. /*                 z = rcos(s)                */
  329. /* If you just figure out how the different s and t's for the different */
  330. /* vectors are connected during rigid rotation this will get you down   */
  331. /* to 6 muls ( 2/vector ). Yet other ways of doing rotation are by     */
  332. /* using Quaternations, Bresenham's algorithms or the Cayley-Klein    */
  333. /* Parameters. Some of these might even give further improvement.    */
  334. /* Another improvement can be achieved using log- and             */
  335. /* exponential-tables. Multiplications can then be replaced with some   */
  336. /* adds and table lookups. This is done as follows:            */
  337. /* a = b * c / d = e^(log(b) + log(c) - log(d))                */
  338. /* and this is a better way than using a division-table.        */
  339. /* Speaking of the division-table, it might also be improved somewhat.  */
  340. /* If you print the values of divT[] you'll notice that many of the     */
  341. /* values are duplicates, this is because we don't shift the values    */
  342. /* enough to notice the difference between some values. So, what you     */
  343. /* might do is to make another table containing only every fourth value */
  344. /* of divT[], this way you'll save alot of memory as the size of divT[] */
  345. /* is reduced by a factor of 4. When using the new table, call it     */
  346. /* div4T[], replace divT[ something ] with div4T[ (int) something/4 ]   */
  347. /* and it should work properly.                        */
  348. /* The ideas presented above has been given almost no thought at all,   */
  349. /* so I will note take any responsibility if something shouldn't work.    */
  350.  
  351. void DrawSolid( short x1, short y1, 
  352.             short x2, short y2, 
  353.             short x3, short y3, 
  354.             short x4, short y4,
  355.             unsigned short ox, 
  356.         unsigned short oy )
  357. {
  358.     mypoints[0].x = ox + x1;
  359.     mypoints[0].y = oy + y1;
  360.     mypoints[1].x = ox + x2;
  361.     mypoints[1].y = oy + y2;
  362.     mypoints[2].x = ox + x3;
  363.     mypoints[2].y = oy + y3;
  364.     mypoints[3].x = ox + x4;
  365.     mypoints[3].y = oy + y4;
  366.  
  367.     XFillPolygon(dpy, win, gc, mypoints, PTS, 
  368.              Convex, CoordModeOrigin); 
  369. }
  370.  
  371. void ClearArea( unsigned short ox, unsigned short oy, unsigned a, unsigned b  )
  372. {
  373.     SetCol("black");
  374.     XFillRectangle(dpy, win, gc, ox - a, oy - a, b, b);
  375. }
  376.  
  377. void SetCol( char * str )
  378. {
  379.       if (XParseColor (dpy, DefaultColormap(dpy,scrn), str, &xcolour))
  380.             if (XAllocColor (dpy, DefaultColormap(dpy,scrn), &xcolour))
  381.             xgcvalues.foreground = xcolour.pixel;
  382.     XChangeGC (dpy,gc,GCForeground, &xgcvalues);
  383. }
  384.  
  385. void Cls( void )
  386. {
  387.         XClearWindow(dpy, win);
  388. }
  389.  
  390. void Init( void )
  391. {
  392.     if (!(dpy = XOpenDisplay (NULL))) 
  393.        {
  394.               fprintf (stderr, "Cannot open display.\n");
  395.               exit(1);
  396.         }
  397.       scrn                 = DefaultScreen(dpy);
  398.     xsetwattrs.backing_store     = Always;
  399.      xsetwattrs.background_pixel     = BlackPixel(dpy,scrn);
  400.     pixel                 = WhitePixel(dpy,scrn);
  401.  
  402.       XChangeWindowAttributes(dpy,RootWindow(dpy,scrn),
  403.                  CWBackingStore|CWBackPixel,
  404.                 &xsetwattrs);
  405.  
  406.     win = RootWindow(dpy,scrn);
  407.     
  408.       gc = XCreateGC (dpy, RootWindow(dpy,scrn),0,NULL);
  409. }
  410.  
  411. void WinInit( int ox, int oy, unsigned a, unsigned b )
  412. {
  413.     win = XCreateSimpleWindow(dpy, RootWindow(dpy,scrn), 
  414.                   ox-a, oy-a, b, b, 0, 
  415.                       WhitePixel(dpy,scrn), BlackPixel(dpy,scrn));
  416.  
  417.     XMapWindow(dpy, win);
  418. }
  419.  
  420. void Exit( void )
  421. {
  422.     XFlush (dpy);
  423. }
  424.  
  425.  
  426.  
  427.